package cn.shengchao.examstar.me.course.acl.adapters.repositories;

import cn.shengchao.examstar.me.course.acl.ports.repositories.IMyCourseRepository;
import cn.shengchao.examstar.me.course.domain.MyCourse;
import cn.shengchao.examstar.me.infrastructure.dao.ProductCourseUserDao;
import cn.shengchao.examstar.me.infrastructure.dao.StudentFreeUnlockCourseNodeDao;
import cn.shengchao.examstar.me.infrastructure.dao.po.StudentFreeUnlockCourseNode;
import cn.shengchao.examstar.me.infrastructure.dao.po.TProductCourseUser;
import cn.shengchao.examstar.me.play.domain.OnlineUser;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import top.continew.starter.cache.redisson.util.RedisUtils;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import static cn.shengchao.base.constant.Constants.RedisKey.COURSE_PROGRESS_LOG_KEY;

@Repository
public class MyCourseRepository implements IMyCourseRepository {
    @Autowired
    private ProductCourseUserDao productCourseUserDao;

    @Autowired
    private StudentFreeUnlockCourseNodeDao studentFreeUnlockCourseNodeDao;


    @Override
    public List<MyCourse> getMyCourseKnowledgeList(Long studentId, Long courseId) {
        return productCourseUserDao.selectList(new LambdaQueryWrapper<TProductCourseUser>()
                .eq(TProductCourseUser::getStudentId, studentId)
                .eq(TProductCourseUser::getDeleted, 0)
                .eq(courseId != -1, TProductCourseUser::getCourseId, courseId)).stream().map(item -> {
            OnlineUser onlineUser = RedisUtils.get(COURSE_PROGRESS_LOG_KEY.formatted(studentId, item
                    .getKnowledgeNodeId()));
            MyCourse myCourse = new MyCourse();
            myCourse.setCourseId(item.getCourseId());
            myCourse.setKnowledgeNodeId(item.getKnowledgeNodeId());
            myCourse.setStudentId(item.getStudentId());
            myCourse.setLastPlayTime(item.getLastPlayTime());
            myCourse.setPlayProgress(Math.toIntExact(onlineUser != null
                    ? onlineUser.getProgress()
                    : item.getLastPlayProgress()));
            myCourse.setUnlockTime(item.getUnlockTime());
            myCourse.setPayId(item.getPayId());
            myCourse.setWatchProgress(item.getWatchProgress());
            myCourse.setWatchLength(item.getWatchLength());
            return myCourse;
        }).toList();
    }

    @Transactional
    @Override
    public void addCourse(String orderId, Long userId, Long courseId, List<Long> knowledgeNodeIds, Long unlockType) {
        List<TProductCourseUser> productCourseUsers = productCourseUserDao.selectList(new LambdaQueryWrapper<TProductCourseUser>()
                .in(TProductCourseUser::getKnowledgeNodeId, knowledgeNodeIds)
                .eq(TProductCourseUser::getCourseId, courseId)
                .eq(TProductCourseUser::getStudentId, userId)
                .eq(TProductCourseUser::getDeleted, 0)
                .orderByDesc(TProductCourseUser::getUnlockTime)
                .last("limit 10")
        );
        Map<Long, List<TProductCourseUser>> map = productCourseUsers.stream().collect(Collectors.groupingBy(TProductCourseUser::getKnowledgeNodeId));
        List<TProductCourseUser> tProductCourseUsers = new ArrayList<>();
        for (Long knowledgeNodeId : knowledgeNodeIds) {
            List<TProductCourseUser> list = map.getOrDefault(knowledgeNodeId, List.of());
            TProductCourseUser productCourseUser = null;
            if (list.size() > 1) {
                list.sort((o1, o2) -> o2.getUnlockTime().compareTo(o1.getUnlockTime()));
            }
            if (!list.isEmpty()) {
                productCourseUser = list.getFirst();
                //重复的设删除标志，以免冲突。
                list.forEach(item -> {
                            item.setDeleted(1L);
                            productCourseUserDao.updateById(item);
                        }
                );
            }
            TProductCourseUser tProductCourseUser = new TProductCourseUser();
            if (productCourseUser != null) {
                tProductCourseUser.setLastPlayProgress(productCourseUser.getLastPlayProgress());
                tProductCourseUser.setLastPlayTime(productCourseUser.getLastPlayTime());
                tProductCourseUser.setWatchLength(productCourseUser.getWatchLength());
                tProductCourseUser.setWatchProgress(productCourseUser.getWatchProgress());
            } else {
                tProductCourseUser.setLastPlayProgress(0);
            }
            tProductCourseUser.setStudentId(userId);
            tProductCourseUser.setCourseId(courseId);
            tProductCourseUser.setKnowledgeNodeId(knowledgeNodeId);
            tProductCourseUser.setPayId(Long.valueOf(orderId));
            tProductCourseUser.setUnlockType(unlockType);
            tProductCourseUser.setUnlockTime(LocalDateTime.now());
            tProductCourseUsers.add(tProductCourseUser);
        }
        productCourseUserDao.insert(tProductCourseUsers);
    }

    @Override
    public List<Long> getMyCourseList(Long uid) {
        return productCourseUserDao.selectList(new LambdaQueryWrapper<TProductCourseUser>()
                .eq(TProductCourseUser::getStudentId, uid)
                .eq(TProductCourseUser::getDeleted, 0)).stream().map(TProductCourseUser::getCourseId).distinct().toList();
    }


    @Override
    public List<Long> getMyCourseListByProductName(Long uid, String productName) {
        return productCourseUserDao.getMyCourseListByProductName(uid, productName);
    }

    @Override
    public Long getStudentSum(List<Long> courseIds) {
        return productCourseUserDao.selectStudentSum(courseIds);
    }

    @Override
    public Long getMyUseFreeUnlockSum(Long id, Long userId) {
        List<StudentFreeUnlockCourseNode> studentFreeUnlockCourseNodes = studentFreeUnlockCourseNodeDao
                .selectList(new LambdaQueryWrapper<StudentFreeUnlockCourseNode>()
                        .eq(StudentFreeUnlockCourseNode::getCourseId, id)
                        .eq(StudentFreeUnlockCourseNode::getUserId, userId));
        return (long) studentFreeUnlockCourseNodes.size();
    }

    @Override
    public void addUseFreeUnlockSumLog(Long courseId, Long courseNodeId, Long userId) {
        StudentFreeUnlockCourseNode studentFreeUnlockCourseNode = new StudentFreeUnlockCourseNode();
        studentFreeUnlockCourseNode.setCourseNodeId(courseNodeId);
        studentFreeUnlockCourseNode.setCourseId(courseId);
        studentFreeUnlockCourseNode.setUserId(userId);
        studentFreeUnlockCourseNodeDao.insert(studentFreeUnlockCourseNode);
    }

    @Override
    public void removeCourse(Long userId, Long productId, List<Long> productItemIds) {
        List<TProductCourseUser> productCourseUser = productCourseUserDao
                .selectList(new LambdaQueryWrapper<TProductCourseUser>().eq(TProductCourseUser::getStudentId, userId)
                        //单独删除课节
                        .in(productItemIds != null && !productItemIds.isEmpty(), TProductCourseUser::getKnowledgeNodeId, productItemIds)
                        //全部删除
                        .eq(TProductCourseUser::getCourseId, productId));
        for (TProductCourseUser tProductCourseUser : productCourseUser) {
            tProductCourseUser.setDeleted(1L);
            productCourseUserDao.updateById(tProductCourseUser);
        }
    }

    @Override
    public @NotNull Map<Long, Set<Long>> getMyCourseKnowledgeSum(Long userId) {
        List<TProductCourseUser> productCourseUsers = productCourseUserDao
                .selectList(new LambdaQueryWrapper<TProductCourseUser>().eq(TProductCourseUser::getDeleted, 0)
                        .eq(TProductCourseUser::getStudentId, userId));
        return productCourseUsers.stream().distinct()
                .collect(Collectors.groupingBy(TProductCourseUser::getCourseId, Collectors.mapping(TProductCourseUser::getKnowledgeNodeId, Collectors.toSet())));
    }

    @Override
    public List<MyCourse> checkUserUnlockedCourseNodes(Long userId, List<Long> courseNodeIds) {
        if (courseNodeIds == null || courseNodeIds.isEmpty()) {
            return List.of();
        }

        List<TProductCourseUser> unlocked = productCourseUserDao.selectList(
                new LambdaQueryWrapper<TProductCourseUser>()
                        .eq(TProductCourseUser::getStudentId, userId)
                        .eq(TProductCourseUser::getDeleted, 0)
                        .in(TProductCourseUser::getKnowledgeNodeId, courseNodeIds)
        );

        return unlocked.stream()
                .map(item -> {
                    OnlineUser onlineUser = RedisUtils.get(COURSE_PROGRESS_LOG_KEY.formatted(userId, item
                            .getKnowledgeNodeId()));
                    MyCourse myCourse = new MyCourse();
                    myCourse.setCourseId(item.getCourseId());
                    myCourse.setKnowledgeNodeId(item.getKnowledgeNodeId());
                    myCourse.setStudentId(item.getStudentId());
                    myCourse.setLastPlayTime(item.getLastPlayTime());
                    myCourse.setPlayProgress(Math.toIntExact(onlineUser != null
                            ? onlineUser.getProgress()
                            : item.getLastPlayProgress()));
                    myCourse.setUnlockTime(item.getUnlockTime());
                    myCourse.setPayId(item.getPayId());
                    myCourse.setWatchProgress(item.getWatchProgress());
                    myCourse.setWatchLength(item.getWatchLength());
                    return myCourse;
                })
                .toList();
    }
}
